#include "BinTextReader.h"
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
#include <algorithm>
#pragma warning(disable : 4996)
#include "..\..\utils\Log.h"
#include "..\..\utils\PlainTextReader.h"
#include "..\utils\StringUtils.h"

template<class T> T fromString(const std::string& s) {
	std::istringstream stream(s);
	T t;
	stream >> t;
	return t;
}
/*
void trim(std::string& str) {
	std::replace(str.begin(), str.end(), '\t', ' ');
	std::string::size_type pos = str.find_last_not_of(' ');
	if (pos != std::string::npos) {
		str.erase(pos + 1);
		pos = str.find_first_not_of(' ');
		if (pos != std::string::npos) {
			str.erase(0, pos);
		}
	} else {
		str.erase(str.begin(), str.end());
	}
}
*/
void splitEntry(const std::string& str, std::vector<std::string>& words) {
	std::string word;
	std::stringstream stream(str);
	while (std::getline(stream, word, ',')) {
		words.push_back(word);
	}
	if ( words.size() == 0 ) {
		words.push_back(str);
	}
}

BinTextReader::BinTextReader() {
	strcpy(m_Empty.name, "EMPTY");
	m_Empty.sectionIndex = 666;
	m_Array = new float[4096];
	m_Offset = 0;
}

BinTextReader::~BinTextReader() {
	delete m_Array;
}

void BinTextReader::saveBinary(const char* fileName) {
	FILE* f;
	f = fopen(fileName, "wb");
	int size = m_Sections.size();
	fwrite(&size, sizeof (int), 1, f);
	int es = m_Entries.size();
	fwrite(&es, sizeof (int), 1, f);
	fwrite(&m_Offset, sizeof (int), 1, f);
	for (size_t i = 0; i < m_Sections.size(); ++i) {
		fwrite(&m_Sections[i], sizeof (Section), 1, f);
	}
	for (size_t i = 0; i < m_Entries.size(); ++i) {
		fwrite(&m_Entries[i], sizeof (Entry), 1, f);
	}
	float* ar = m_Array;
	for (int i = 0; i < m_Offset; ++i) {
		fwrite(&(*ar), sizeof (float), 1, f);
		++ar;
	}
	fclose(f);
}

void BinTextReader::readBinary(const char* fileName) {
	FILE* f;
	f = fopen(fileName, "rb");
	int size = 0;
	fread(&size, sizeof (int), 1, f);
	int es = 0;
	fread(&es, sizeof (int), 1, f);
	int ar = 0;
	fread(&ar, sizeof (int), 1, f);
	for (size_t i = 0; i < size; ++i) {
		Section s;
		fread(&s, sizeof (Section), 1, f);
		m_Sections.push_back(s);
	}
	for (int i = 0; i < es; ++i) {
		Entry e;
		fread(&e, sizeof (Entry), 1, f);
		m_Entries.push_back(e);
	}
	float t;
	for (int i = 0; i < ar; ++i) {
		fread(&t, sizeof (float), 1, f);
		m_Array[i] = t;
	}
	fclose(f);
}

void BinTextReader::readFile(const char* fileName) {
	std::ifstream ifs(fileName);
	std::string temp;
	int idx = 0;
	int currentIndex = -1;
	if (ifs.is_open()) {
		int braces = 0;
		while (std::getline(ifs, temp)) {
			// starts a new Sector
			if (temp.find("{", 0) != std::string::npos) {
				std::string::size_type loc = temp.find("{", 0);
				std::string name = temp.substr(0, loc);
				ds::string::trim(name);
				Section s;
				strcpy(s.name, name.c_str());
				s.index = idx;
				currentIndex = idx;
				++idx;
				m_Sections.push_back(s);
				++braces;
			} else if (temp.find("}", 0) != std::string::npos) {
				--braces;
			} else {
				std::string::size_type loc = temp.find(":", 0);
				std::string name = temp.substr(0, loc);
				ds::string::trim(name);
				std::string right = temp.substr(loc + 1);
				ds::string::trim(right);
				int size = determineSize(right);
				Entry e;
				strcpy(e.name, name.c_str());
				e.size = size;
				loc = right.find("\"");
				if (loc != std::string::npos) {
					std::string value = right.substr(loc + 1);
					loc = value.find("\"");
					value = value.substr(0, loc);
					e.offset = saveString(value);
					e.size = value.length();
				} else {
					e.offset = saveData(right);
				}
				e.sectionIndex = currentIndex;
				m_Entries.push_back(e);
			}
		}
		ifs.close();
		if (braces != 0) {
			LOG(logERROR) << "Unmatching braces found";			
		}
	}
}

int BinTextReader::determineSize(const std::string& line) {
	std::vector<std::string> words = ds::string::split(line);
	return words.size();
}

int BinTextReader::saveData(const std::string& value) {
	int idx = m_Offset;
	std::vector<std::string> words = ds::string::split(value);
	for (size_t i = 0; i < words.size(); ++i) {
		std::string val = words[i];
		float t = fromString<float>(val);
		m_Array[m_Offset] = t;
		++m_Offset;
	}    
	return idx;
}

int BinTextReader::saveString(const std::string& value) {
	int idx = m_Offset;
	for (size_t i = 0; i < value.length(); ++i) {
		m_Array[m_Offset] = value[i];
		++m_Offset;
	}
	m_Array[m_Offset] = 0;
	++m_Offset;
	return idx;
}

bool BinTextReader::contains(const char* name) const {
	for (size_t i = 0; i < m_Sections.size(); ++i) {
		if (strcmp(name, m_Sections[i].name) == 0) {
			return true;
		}
	}
	return false;
}

Entry& BinTextReader::getEntry(const char* name, int sectionIndex) {
	for (size_t i = 0; i < m_Entries.size(); ++i) {
		Entry* e = &m_Entries[i];
		if (e->sectionIndex == sectionIndex && strcmp(e->name, name) == 0) {
			return m_Entries[i];
		}
	}
	return m_Empty;
}

float BinTextReader::getFloat(const char* name, int sectionIndex) {
	Entry& e = getEntry(name, sectionIndex);
	if (e.name != "EMPTY") {
		float f = m_Array[e.offset];
		return f;
	}
	return 0.0f;
}

void BinTextReader::getString(const char* name, char* buffer, int sectionIndex) {
	Entry& e = getEntry(name, sectionIndex);
	if (e.name != "EMPTY") {
		float* ar = (m_Array + e.offset);
		while (*ar != 0) {
			*buffer = (char) *ar;
			++ar;
			++buffer;
		}
		*buffer = '\0';
	}
}

std::string BinTextReader::getString(const char* name, int sectionIndex) {
	Entry& e = getEntry(name, sectionIndex);
	char buffer[20];
	int cnt = 0;
	if (e.name != "EMPTY") {
		float* ar = (m_Array + e.offset);
		while (*ar != 0) {
			buffer[cnt] = (char) *ar;
			++ar;
			++cnt;
		}
		buffer[cnt] = '\0';
	}
	std::string tst(buffer);
	return tst;
}

